package org.gap.jseed;

import static java.util.Arrays.asList;
import static org.gap.jseed.ContainerFactoryHelper.emptyListOfBuilderFactories;
import static org.gap.jseed.ContainerFactoryHelper.emptyListOfInjectorFactories;

import org.gap.jseed.bean.annotation.Bean;
import org.gap.jseed.injection.ClassWriter;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class ServiceFactoryEndToEnd {

	private SeedContainer container;

	@Before
	public void createSeedContainer() {
		container = SeedContainerFactory.newContainer();
	}
	
	@After
	public void removeSystemProperty() {
		System.setProperty("org.gap.jseed.ClassNameService", "");
	}
	
	@Test
	public void Able_to_add_new_builder_factories() throws Exception {
		container = SeedContainerFactory.newContainer(asList(withBuilderFactory()), emptyListOfInjectorFactories());
		Subject subject = container.create(Subject.class);
		isOfInstance(subject, SubjectImpl.class);
	}
	
	@Test
	public void User_provided_classnames_can_be_given_and_used_externally_to_jseed() throws Exception {
		String newProxyName = "org.gap.jseed.ServiceFactoryEndToEnd$SimpleBeanSeedProxy";
		System.setProperty("org.gap.jseed.ClassNameService", "org.gap.jseed.ServiceFactoryEndToEnd$ClassNameServiceImpl");
		container.register(SimpleBean.class);
		
		SimpleBean instance = container.get(SimpleBean.class);
		Assert.assertEquals(instance.getClass().getName(), newProxyName);
		Assert.assertNotNull("Sanity check to ensure SimpleBean exists", Class.forName("org.gap.jseed.ServiceFactoryEndToEnd$SimpleBean"));
		Class<?> injectedClass = Class.forName(newProxyName);
		Assert.assertNotNull(injectedClass.newInstance());
	}

	private BuilderFactory withBuilderFactory() {
		return new BuilderFactory() {
			@SuppressWarnings({ "unchecked" })
			@Override
			public <T> Builder<T> createBuilder(Builder<T> builder) {
				return new Builder<T>() {
					@Override
					public Class<? extends T> getType() {
						return (Class<? extends T>) Object.class;
					}

					@Override
					public T createInstance() {
						return (T) new SubjectImpl();
					}
				};
			}
		};
	}

	private void isOfInstance(Subject subject, Class<SubjectImpl> class1) {
		Assert.assertTrue("Should be of type [" + class1 + "] but was [" + subject.getClass() + "]", class1.isInstance(subject));
	}

	@Test
	public void Able_to_add_new_injector_factories() throws Exception {
		boolean[] wasCalled = {false};
		SeedContainer container = SeedContainerFactory.newContainer(emptyListOfBuilderFactories(), asList(withInjectorFactory(wasCalled)));
		container.create(Subject.class);
		Assert.assertTrue("Injection factory was called", wasCalled[0]);
	}

	private Injector withInjectorFactory(final boolean[] validator) {
		return new Injector() {
			
			@Override
			public void inject(ClassWriter writer, Class<?> source) {
				validator[0] = true;
			}
		};
	}

	public interface Subject {
	}

	public static class SubjectImpl implements Subject {
	}
	
	@Bean
	public static abstract class SimpleBean {
		public abstract void setField(String value);
		public abstract String getField();
	}
	
	public static class ClassNameServiceImpl implements ClassNameService {

		@Override
		public String createName(String name) {
			return name + "SeedProxy";
		}
		
	}
}
